﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using System.Threading;

namespace CoffeePotServer
{
    /// <summary>
    /// File: Pot.cs
    /// Author: James Dibble 10009689
    /// Last Revision: 17/03/12
    /// SVN Address:
    /// http://code.google.com/p/cnos-coffeepot-server/source/browse/branches/DibbleBranch/CoffeePotServerLib/CoffeePotServerLib/Pot.cs
    /// </summary>
    public class Pot
    {
        #region Constants
        private const double ROOM_TEMP = 28.0;
        private const double BOILING_TEMP = 100.0;
        //http://www.burnsjournal.com/article/S0305-4179(07)00255-0/abstract
        private const double ACCEPTABLE_TEMP = 60.0;
        #endregion

        #region Attributes
        private PotState _potState;
        private int _intID;
        private Client_Handler _owner;
        private BackgroundWorker bwCoolCup = new BackgroundWorker();
        private BackgroundWorker bwPour = new BackgroundWorker();
        private double _dblCurrentTemp;
        private List<Addition> _currAdditions;
        #endregion

        #region Constructor
        /// <summary>
        /// Create a new virtual Coffee Pot.
        /// </summary>
        /// <param name="ID">The pot designator</param>
        public Pot(int designator)
        {
            _intID = designator;
            _potState = PotState.OK;
            bwCoolCup.DoWork += new DoWorkEventHandler(CoolCup);
            bwCoolCup.WorkerSupportsCancellation = true;
            bwPour.WorkerSupportsCancellation = true;
            _dblCurrentTemp = ROOM_TEMP;
            _currAdditions = new List<Addition>();
        }
        #endregion

        #region Properties
        /// <summary>
        /// The unique designator of the coffee pot.
        /// </summary>
        public int Designator
        {
            get
            {
                return _intID;
            }
        }

        public PotState State
        {
            get { return _potState; }
            set { _potState = value; }
        }

        public Client_Handler Owner
        {
            get { return _owner; }
            set { _owner = value; }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Simulate a coffee pot brewing.  The speed of brewing is determined by the CPU speed of the host.
        /// </summary>
        public void Brew()
        {
            _currAdditions.Clear();
            Console.WriteLine("\tPot {0} is brewing", _intID);
            if (bwCoolCup.IsBusy) bwCoolCup.CancelAsync();
            this._potState = PotState.STILL_BREWING;
            BackgroundWorker bwBrew = new BackgroundWorker();
            bwBrew.DoWork += delegate
            {
                int intCount = 0;
                while (_dblCurrentTemp < BOILING_TEMP)
                {
                    _dblCurrentTemp = (Math.Log(intCount, Math.E) / 0.10 + 28);
                    intCount++;
                    Thread.Sleep(5);
                }
                this._potState = PotState.CUP_HOT;
                Console.WriteLine("\tPot {0} has boiled.", _intID);
                this.bwCoolCup.RunWorkerAsync();
            };
            bwBrew.RunWorkerAsync();
        }

        /// <summary>
        /// Simulate a coffee pot brewing.  The speed of brewing is determined by the CPU speed of the host.
        /// </summary>
        /// <param name="addTheseAdditions">The requested additions from the client.</param>
        public void Brew(List<Addition> addTheseAdditions)
        {
            _currAdditions.Clear();
            _currAdditions = addTheseAdditions;
            Console.WriteLine("\tPot {0} is brewing", _intID);
            if (bwCoolCup.IsBusy) bwCoolCup.CancelAsync();
            this._potState = PotState.STILL_BREWING;
            BackgroundWorker bwBrew = new BackgroundWorker();
            bwBrew.DoWork += delegate
            {
                int intCount = 0;
                while (_dblCurrentTemp < BOILING_TEMP)
                {
                    _dblCurrentTemp = (Math.Log(intCount, Math.E) / 0.10 + 28);
                    intCount++;
                    Thread.Sleep(5);
                }
                this.bwCoolCup.RunWorkerAsync();
                Console.WriteLine("\tPot {0} has boiled.  Current temp: {1} Celcius.", _intID, Math.Round(_dblCurrentTemp, 2));
                this._potState = PotState.CUP_HOT;
                Pour();
            };
            bwBrew.RunWorkerAsync();
        }

        /// <summary>
        /// Simulate pouring an addition.
        /// </summary>
        public void Pour()
        {
            Console.WriteLine("\tPot {0} is adding {1}.", _intID, _currAdditions[0].Type);
            _currAdditions.RemoveAt(0);
            this._potState = PotState.POURING;
            bwPour.RunWorkerAsync();
        }

        /// <summary>
        /// Tell the coffee pot to stop pouring an addition.
        /// </summary>
        public void When()
        {
            if (this.State != PotState.POURING)
            {
                if(this._potState == PotState.OVERFLOWING)
                    throw new ClientResponseException("Pot has overflowed.",
                                                  ClientResponseException.ExceptionType.OVERFLOWED);
                throw new ClientResponseException("Pot was not pouring.",
                                                  ClientResponseException.ExceptionType.NOTPOURING);
            }
            bwPour.CancelAsync();

            if (_currAdditions.Count == 0)
            {
                this._potState = PotState.OK;
                this._owner = null;
            }
            else
            {
                this._potState = PotState.POURING;
                this.Pour();
            }
        }

        /// <summary>
        /// Simulate a coffee cup cooling down after it has brewed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CoolCup(object sender, DoWorkEventArgs e)
        {
            int intCount = 0;
            Boolean bolNotified = false;
            while (_dblCurrentTemp > ROOM_TEMP)
            {
                _dblCurrentTemp = (Math.Exp(1 / (intCount * 0.00001))) + 28;
                intCount++;
                if (_dblCurrentTemp < ACCEPTABLE_TEMP && !bolNotified)
                {
                    Console.WriteLine("\tPot {0} is now cold.  Current temp: {1} Celcius", _intID, Math.Round(_dblCurrentTemp, 2));
                    this._potState = PotState.CUP_COLD;
                    bolNotified = true;
                }
                Thread.Sleep(5);
            }
            this._owner = null;
            this._potState = PotState.OK;
        }

        /// <summary>
        /// Simulate pouring an addition.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PourAddition(object sender, DoWorkEventArgs e)
        {
            Thread.Sleep(10000);
            this._potState = PotState.OVERFLOWING;
            Console.WriteLine("Pot {0} has overflowed.", _intID);
        }
        #endregion

        #region Enums
        /// <summary>
        /// The status of the coffee pot.
        /// </summary>
        public enum PotState
        {
            [Description("510 Pot busy")]
            BUSY,
            [Description("406 Not Acceptable")]
            NOT_ACEEPT,
            [Description("421 Not pouring")]
            NOT_POURING,
            [Description("422 Still pouring")]
            STILL_POURING,
            [Description("505 Still Brewing")]
            STILL_BREWING,
            [Description("506 Already Added")]
            ALREAD_ADD,
            [Description("202 Accepted")]
            POURING,
            [Description("419 Coffee gone cold")]
            CUP_COLD,
            [Description("200 OK")]
            OK,
            [Description("200 OK")]
            CUP_HOT,
            [Description("515 Overflowing")]
            OVERFLOWING
        }
        #endregion
    }
}